﻿using huqiang.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace huqiang.Communication
{
    public class KcpServer : KcpListener
    {
        public struct SocMsg2 : IClear, IDisposable
        {
            internal byte[] dat;
            public int len;
            public UInt16 time;
            public KcpLink link;

            public void Clear()
            {
                dat = null;
                link = null;
            }

            public void Dispose()
            {
                if (dat != null)
                    DataPack32.RecycleBuffer(dat);
                dat = null;
                link = null;
            }
        }
        /// <summary>
        /// 创建新的客户端连接
        /// </summary>
        public Func<KcpLink> CreateNew = () => { return new KcpLink(); };
        /// <summary>
        /// 白名单
        /// </summary>
        public static List<int> Whitelist = new List<int>();
        /// <summary>
        /// 黑名单
        /// </summary>
        public static List<int> Blacklist = new List<int>();

        NetworkContent[] ncs = new NetworkContent[32];
        List<BroadcastMsg> datas = new List<BroadcastMsg>();
        public static UInt16 MinID = 60000;
        public static UInt16 MaxID = 64000;
        public static UInt16 SendTime;
        /// <summary>
        /// 消息超时重传时间默认 500毫秒
        /// </summary>
        public int MsgTimeOut = 500;
        /// <summary>
        /// 允许文件传输
        /// </summary>
        public bool AllowFileTransfer = false;
        static Random random = new Random();
        LockBuffer<KcpLink> linkBuff;
        //LockQueueS<SocMsg2> queue = new LockQueueS<SocMsg2>(4096);
        Queue<SocMsg2> socMsgs = new Queue<SocMsg2>(4096);
        SpinLock spin = new SpinLock();
        //int tCount = 0;
        /// <summary>
        /// 拒绝外部发起的新连接
        /// </summary>
        public bool RejectAutoConnections = false;
        public KcpServer()
        {
           
        }
        public void Run(int port = 0, int userBuffer = 2048)
        {
            if (running)
            {
                return;
            }
            linkBuff = new LockBuffer<KcpLink>(userBuffer);
            Start(port);
        }
        public void Close()
        {
            soc.Close();
        }
        //设置用户的udp对象用于发送消息
        public KcpLink FindOrCreateLink(IPEndPoint ep)
        {
            var b = ep.Address.GetAddressBytes();
            int ip = 0;
            unsafe
            {
                fixed (byte* bp = &b[0])
                    ip = *(Int32*)bp;
            }
            var link = FindLink(ip, ep.Port);
            if (link == null)
            {
                link = CreateNew();
                byte[] key = new byte[32];
                random.NextBytes(key);
                link.Key = key;
                byte[] iv = new byte[16];
                random.NextBytes(iv);
                link.Iv = iv;
                link.AllowFileTransfer = AllowFileTransfer;
                link.kcp = this;
                link.ConnectTime = DateTime.Now.Ticks;
                link.MsgTimeOut = MsgTimeOut;
                link.endPoint = ep;
                link.port = ep.Port;
                link.ip = ip;
                link.buffIndex = linkBuff.Count;
                linkBuff.Add(link);
                link.Awake();
                soc.SendTo(Heart, ep);
            }
            link.RecvTime = DateTime.Now.Ticks;
            return link;
        }
        /// <summary>
        /// 接收的线程
        /// </summary>
        /// <param name="dat"></param>
        /// <param name="len"></param>
        /// <param name="ep"></param>
        public override void Dispatch(byte[] dat, int len, IPEndPoint endPoint)
        {
            var b = endPoint.Address.GetAddressBytes();
            int ip = 0;
            unsafe
            {
                fixed (byte* bp = &b[0])
                    ip = *(Int32*)bp;
            }
            if (Blacklist.Contains(ip))
                return;
            KcpLink link;
            if (RejectAutoConnections)
            {
                if (Whitelist.Contains(ip))
                    link = FindOrCreateLink(endPoint);
                else link = FindLink(ip, Port);
            }
            else
            {
                link = FindOrCreateLink(endPoint);
            }
            if (link != null)
            {
                now = DateTime.Now.Ticks;
                for (int i = 0; i < datas.Count; i++)
                {
                    datas[i].SendCount = 0;
                }
                long t = now / 10000;//将单位时间设为毫秒
                long r = t % 30000;//设置循环时间为30000/1000=30秒

                UInt16 time = (UInt16)r;

                link.RecvTime = now;
                if (len > 9)
                {
                    link.AddRecvMsg(dat,len);//.ReciveMsg(dat, len, link);// kcp.UnPack(time);
                    HandleMsg(link,time);
                }
                if (!link._connect)
                {
                    link._connect = true;
                    link.Connect();
                }
                else
                {
                    link._connect = true;
                }
            }
        }
        void HandleMsg(KcpLink link2, UInt16 time)
        {
            var rBuf = link2.RecvBuffer;
            int start = 0;
            int end = link2.RecvLen;
            while (DataPack32.GetPart(rBuf, ref start, ref end))
            {
                int l = end - start;
                if (l > 4 & l <= 1600)
                {
                    byte[] dat = DataPack32.GetBuffer(l);
                    int c =DataPack32.UnpackInt32(rBuf, start, end, dat);
                    SocMsg2 soc = new SocMsg2();
                    soc.dat = dat;
                    soc.len = c;
                    soc.time = time;
                    soc.link = link2;
                    bool gotLock = false;
                    try
                    {
                        spin.Enter(ref gotLock);
                        socMsgs.Enqueue(soc);
                    }
                    finally
                    {
                        if (gotLock)
                            spin.Exit();
                    }
                    //queue.Enqueue(soc);
                }
                start = end + 4;
                end = link2.RecvLen;
            }
            if (start == 0)
                return;
            int rc = link2.RecvLen - start;
            for (int i = 0; i < rc; i++)
            {
                link2.RecvBuffer[i] = link2.RecvBuffer[end];
                end++;
            }
            link2.RecvLen = rc;
            if (link2.RecvLen > 2048)
                link2.RecvLen = 0;
        }
        public override void RemoveLink(NetworkLink link)
        {
            linkBuff.Remove(link as KcpLink);
        }
        public override void Dispose()
        {
            base.Dispose();
        }
        public KcpLink FindLink(Int64 id)
        {
            return linkBuff.Find((link) => {
                if (link.id == id)
                    return true;
                return false;
            });
        }
        public KcpLink FindLink(int ip, int port)
        {
            return linkBuff.Find((link) => {
                if (link.ip == ip)
                    if (link.port == port)
                        return true;
                return false;
            });
        }
        /// <summary>
        /// 开启心跳,防止超时断线
        /// </summary>
        public void OpenHeart()
        {
            heart = true;
        }
        /// <summary>
        /// 关闭心跳
        /// </summary>
        public void CloseHeart()
        {
            heart = false;
        }
        UInt16 bid = 60000;
        public override bool PostBroadcast(byte[] dat, int spin)
        {
            if (dat.Length > 32 * DataPacker.FragmentDataSize)
            {
                throw new Exception("超出允许的封包大小：" + 32 * DataPacker.FragmentDataSize);
            }
            int id = Thread.CurrentThread.ManagedThreadId;
            for (int i = 0; i < 32; i++)
            {
                if (ncs[i].State == 0)
                {
                    ncs[i].State = 1;
                    ncs[i].ThreadID = id;
                    for (int j = 0; j < spin; j++)
                        if (ncs[i].ThreadID != id)
                            goto label;
                    ncs[i].dat = dat;
                    ncs[i].State = 2;
                    return true;
                }
                label:;
            }
            return false;
        }
        bool GetBroadcast(ref NetworkContent content)
        {
            for (int i = 0; i < 32; i++)
            {
                if (ncs[i].State == 2)
                {
                    content = ncs[i];
                    ncs[i].dat = null;
                    ncs[i].Bid = 0;
                    ncs[i].State = 0;
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 发送的线程
        /// </summary>
        protected override void ProBroadcast()
        {
            NetworkContent nc = new NetworkContent();
            int id = sendThread.ManagedThreadId;
            long now = DateTime.Now.Ticks / 10000;
            long r = now % 30000;
            Int16 time = (Int16)r;
            //while (GetBroadcast(ref nc))
            //{
            //    var dat = kcp.Pack(0, nc.dat, bid, time);
            //    BroadcastMsg msg = new BroadcastMsg();
            //    msg.MsgID = bid;
            //    msg.data = dat;
            //    datas.Add(msg);
            //    linkBuff.Execute((link) => { link.FrocePost(id, null, bid); });
            //    bid++;
            //    if (bid >= MaxID)
            //        bid = MinID;
            //}
        }
        public override BroadcastMsg FindBroadcastMsg(int msgID)
        {
            for (int i = 0; i < datas.Count; i++)
            {
                if (datas[i].MsgID == msgID)
                {
                    return datas[i];
                }
            }
            return null;
        }
        public override void UpdateBroadcastMsg(int msgID)
        {
            for (int i = 0; i < datas.Count; i++)
            {
                if (datas[i].MsgID == msgID)
                {
                    datas[i].SendTime = now;
                    datas[i].SendCount++;
                    return;
                }
            }
        }
        bool heart;

        long now, last;
        /// <summary>
        /// 发送的线程
        /// </summary>
        public override void SendAll()
        {
            now = DateTime.Now.Ticks;
            for (int i = 0; i < datas.Count; i++)
            {
                datas[i].SendCount = 0;
            }
            NowMillisecond = now / 10000;//将单位时间设为毫秒
            long r = NowMillisecond % 30000;//设置循环时间为30000/1000=30秒

            UInt16 time = (UInt16)r;
            SendTime = time;
            int qc = socMsgs.Count;
            SocMsg2 msg = new SocMsg2();
            for (int i = 0; i < qc; i++)
            {
                bool gotLock = false;
                try
                {
                    spin.Enter(ref gotLock);
                    msg = socMsgs.Dequeue();
                }
                finally
                {
                    if (gotLock)
                        spin.Exit();
                }
                msg.link.dataPacker.HandleRecv(msg.dat, msg.len, msg.time);
                msg.Dispose();
            }
            if (heart)
            {
                long c = NowMillisecond / 1000;//当前时间，单位秒
                if (c > last)//需要给当前没有消息的用户发送心跳包
                {
                    last = c;
                    for (int i = 0; i < linkBuff.Count; i++)
                    {
                        var l = linkBuff[i];
                        if (l != null)
                        {
                            soc.SendTo(Heart, l.endPoint);
                            l.ContinuousSend();
                        }
                    }
                }
                else//发送用户的消息
                {
                    for (int i = 0; i < linkBuff.Count; i++)
                    {
                        var l = linkBuff[i];
                        if (l != null)
                        {
                            l.HandleSendStack(time);
                            l.ResendTimeOut(soc, NowMillisecond);
                            l.ContinuousSend();
                        }
                    }
                }
            }
            else//发送用户的消息
            {
                for (int i = 0; i < linkBuff.Count; i++)
                {
                    var l = linkBuff[i];
                    if (l != null)
                    {
                        l.HandleSendStack(time);
                        l.ResendTimeOut(soc, now);
                        l.ContinuousSend();
                    }
                }
            }
            for (int c = datas.Count - 1; c >= 0; c--)
            {
                if (datas[c].SendCount == 0)
                {
                    if (now - datas[c].SendTime > 10 * 1000 * 10000)//清除超过10秒未被使用的广播消息
                    {
                        var bm = datas[c];
                        bm.Dispose();
                        datas.RemoveAt(c);
                    }
                }
            }
        }
        protected override void ManageLinks()
        {
            long now = DateTime.Now.Ticks;
            for (int i = linkBuff.Count - 1; i >= 0; i--)
            {
                var link = linkBuff[i];
                if (link != null)
                {
                    if (now - link.RecvTime > TimeOut)
                        if (link.Disconnect())
                        {
                            link.RecyclingTime = now;
                            link.Dispose();
                            linkBuff.RemoveAt(i);
                        }
                }
                else
                {
                    linkBuff.RemoveAt(i);
                }
            }
        }
        public void GetAllLinks<T>(List<T> links) where T : KcpLink, new()
        {
            linkBuff.Execute((o) => { links.Add(o as T); });
        }
    }
}
